package cn.itsource.ymcc.service.impl;

import cn.itsource.exception.GlobalException;
import cn.itsource.result.JSONResult;
import cn.itsource.util.AssertUtil;
import cn.itsource.util.CodeGenerateUtils;
import cn.itsource.ymcc.domain.Course;
import cn.itsource.ymcc.domain.CourseMarket;
import cn.itsource.ymcc.domain.CourseOrder;
import cn.itsource.ymcc.domain.CourseOrderItem;
import cn.itsource.ymcc.dto.CourseOrderParamDto;
import cn.itsource.ymcc.dto.Order2PayParamDto;
import cn.itsource.ymcc.dto.PayAliNotifyDto;
import cn.itsource.ymcc.dto.PreCourseOrder;
import cn.itsource.ymcc.feign.CourseClient;
import cn.itsource.ymcc.feign.CourseUserLearnClient;
import cn.itsource.ymcc.mapper.CourseOrderMapper;
import cn.itsource.ymcc.service.ICourseOrderItemService;
import cn.itsource.ymcc.service.ICourseOrderService;
import cn.itsource.ymcc.vo.Course2OrderInfoVo;
import cn.itsource.ymcc.vo.Course2OrderVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.log4j.Log4j;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Cherish
 * @since 2022-08-18
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {

    public static final String TOKEN_KEY = "token:%s:%s";

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
   private CourseClient courseClient;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private ICourseOrderItemService courseOrderItemService;
    /**
     * 1.参数校验
     * 2.token校验
     * 3.下单逻辑
     *  3.1 各种业务校验，不能下单一个下架的课程
     * 4.创建订单，保存数据库
     * 5.删除防重复token
     * 5.。。。。支付
     * @param dto
     */
    @Override
    @Transactional
    public String placeOrder(CourseOrderParamDto dto) {

       // 测试redis是否有效
//        redisTemplate.opsForValue().set("test:1:2","是否能存入redis");
//        Object o = redisTemplate.opsForValue().get("test:1:2");
//        System.out.println(o);

        //1.参数校验 JSR303
        //2.token校验
        Long loginId = 3L;
        // List<Long> courseIds;  //["9", "3"]  --> "9,3"
        String courseIdsStr = StringUtils.join(dto.getCourseIds(), ",");
        String key = String.format(TOKEN_KEY,loginId,courseIdsStr);
        Object tokenObj = stringRedisTemplate.opsForValue().get(key);
        String s = tokenObj.toString();
        String substring = s.substring(1, s.length() - 1);
        AssertUtil.isNotNull(tokenObj, "请勿重复下单！！！");
        boolean tokenOk = dto.getToken().equals(substring);
        AssertUtil.isTrue(tokenOk, "token错误！！");
        //3.下单逻辑
        //3.1 订单服务通过feign接口查询课程  course courseMarket
        JSONResult jsonResult = courseClient.course2OrderInfo(courseIdsStr);
        AssertUtil.isTrue(jsonResult.isSuccess(), jsonResult.getMessage());
        Object data = jsonResult.getData();
        AssertUtil.isNotNull(data, "课程不存在，下单失败！");
        Course2OrderInfoVo daVo = JSONObject.parseObject(JSON.toJSONString(data), Course2OrderInfoVo.class);
        List<Course2OrderVo> xiaoVos = daVo.getCourseInfos();
        Date now = new Date();
        //================创建订单
        CourseOrder order = new CourseOrder();
        order.setCreateTime(now);
        order.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        order.setTotalAmount(daVo.getTotalAmount());
        order.setStatusOrder(CourseOrder.STATE_WAIT_PAY);//待支付
        order.setUserId(loginId);
        order.setPayType(dto.getPayType());
        //insert(order);

        //==============创建订单明细
        // 3.2 各种业务校验，不能下单一个下架的课程
        //4.创建订单，保存数据库
        StringBuilder sb = new StringBuilder();
        sb.append("课程【");
        xiaoVos.stream().forEach(vo -> {
            Course course = vo.getCourse();
            CourseMarket courseMarket = vo.getCourseMarket();
            boolean isOnLine = course.getStatus() == Course.STATE_ON_LINE;
            AssertUtil.isTrue(isOnLine, "课程状态非法！");
            CourseOrderItem item = new CourseOrderItem();
            item.setOrderId(order.getId());
            item.setAmount(courseMarket.getPrice());
            item.setCount(1);
            item.setCreateTime(now);
            item.setCourseId(course.getId());
            item.setCourseName(course.getName());
            item.setCoursePic(course.getPic());
            item.setOrderNo(order.getOrderNo());
            //courseOrderItemService.insert(item);

            order.setTotalCount(order.getTotalCount() + 1);
            sb.append(course.getName()).append("-");
            order.getItems().add(item);
        });
        sb.append("】订单");
        order.setTitle(sb.toString());
        //updateById(order);
        //5.发送保存支付单的事务消息
        /*
          message：消息内容？  支付单需要的数据
               支付金额、支付类型、订单ID\订单号 userId  主题
          arg:扩展的参数  用来传递给事务监听器，执行本地事务的数据
         */
        Map<String, Object> map = new HashMap<>();
        map.put("loginId",loginId);
        map.put("courseId",courseIdsStr);

        Order2PayParamDto order2PayParamDto = new Order2PayParamDto(
                order.getTotalAmount(),
                dto.getPayType(),
                order.getOrderNo(),
                order.getUserId(),
                JSON.toJSONString(map),
                order.getTitle()
        );

        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(order2PayParamDto)).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxOrder2PayGroup",//同监听器组名
                "order-topic:payorder-tags",
                message,
                order
        );

        //执行本地事务的状态
        log.info("发送事务消息开始");
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        //发送消息状态
        SendStatus sendStatus = sendResult.getSendStatus();
        log.info("发送事务消息完成");

        if( localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            throw new GlobalException("下单失败！请重新下单","401");
        }

        //5.删除防重复token
        stringRedisTemplate.delete(key);


        //7.发送支付超时取消订单延迟消息    要做异常捕获，自己把异常处理了（兜底）
//        try {
//            Message<String> delayMessage = MessageBuilder.withPayload(order.getOrderNo()).build();
//            SendResult syncSend = rocketMQTemplate.syncSend(
//                    "cancel-order-topic:paytimeout-tags",
//                    delayMessage,
//                    300000,
//                    5  //30秒
//            );
//            SendStatus sendSendStatus = syncSend.getSendStatus();
//            if(sendSendStatus != SendStatus.SEND_OK){
//                //发送消息失败  兜底
//                //1.重试3次
//                //2.讲消息体保存到数据库（提供管理界面，手动出发发布消息），发钉钉，短信，邮件通知运维人工介入处理
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//            //发送消息失败  兜底
//            //1.重试3次
//            //2.将消息体保存到数据库（提供管理界面，手动出发发布消息），发钉钉，短信，邮件通知运维人工介入处理
//        }

        return order.getOrderNo();
    }


    @Override
    public void saveOrderAndItems(CourseOrder courseOrder) {
        //1.业务校验
        CourseOrder courseOrderTmp = selectByOrderNo(courseOrder.getOrderNo());
        AssertUtil.isNull(courseOrderTmp,"订单已经存在，请勿重复下单");
        insert(courseOrder);
        for (CourseOrderItem item : courseOrder.getItems()) {
            item.setOrderId(courseOrder.getId());//一定不能忘记
            courseOrderItemService.insert(item);
        }
    }

    @Override
    public CourseOrder selectByOrderNo(String orderNo) {
        Wrapper<CourseOrder> wrapper= new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }

    @Override
    public void updateOrderByPayNotify(PayAliNotifyDto dto) {
        //需要完成各种业务幂等性处理
        CourseOrder courseOrder = selectByOrderNo(dto.getOrderNo());
        if(courseOrder.getStatusOrder() == CourseOrder.STATE_WAIT_PAY){
            courseOrder.setStatusOrder(CourseOrder.STATE_SUCCESS);
            courseOrder.setUpdateTime(new Date());
            updateById(courseOrder);
        }else{
            //  === 有些业务bug，没办法系统直接处理，那么就需要由人工介入
            //你支付成功了，但是订单的状态不对了
            //还没有支付成功，就被手动取消了  应该执行退款逻辑
            //1.记录支付结果处理异常日志
            //2.直接给负责人钉钉，手机，邮箱，发异常处理消息，由人工介入处理
            log.info("人工服务");
        }
    }

    @Override
    public void cancelOrderByPayTimeOut(String orderNo) {
        CourseOrder courseOrder = selectByOrderNo(orderNo);
        if(courseOrder == null){
            return;
        }
        //1.只有是待支付才能取消
        boolean isWaitPay = courseOrder.getStatusOrder() == CourseOrder.STATE_WAIT_PAY;
        if(!isWaitPay){
            return;//2.如果是支付了，就不要执行定时任务
        }
        courseOrder.setStatusOrder(CourseOrder.STATE_CANCEL);
        courseOrder.setUpdateTime(new Date());
        updateById(courseOrder);
    }

    @Override
    public String killPlaceOrder(CourseOrderParamDto dto) {
        // 参数校验
        String orderNo = dto.getOrderNo();
        AssertUtil.isNotNull(orderNo,"订单号不能为空！！！");
        Long loginId = 3L;
        PreCourseOrder preCourseOrder = (PreCourseOrder) redisTemplate.opsForValue().get(orderNo);
        String key = String.format(TOKEN_KEY,loginId,preCourseOrder.getCourseId());
        Object tokenObj = redisTemplate.opsForValue().get(key);
        AssertUtil.isNotNull(tokenObj,"请勿重复下单！！！");
        boolean tokenOk = dto.getToken().equals(tokenObj.toString());
        AssertUtil.isTrue(tokenOk,"token错误！！");

        Date now = new Date();
        //创建订单
        CourseOrder order = new CourseOrder();
        order.setCreateTime(now);
        order.setOrderNo(preCourseOrder.getOrderNo());
        order.setTotalAmount(preCourseOrder.getTotalAmount());
        order.setStatusOrder(CourseOrder.STATE_WAIT_PAY);//待支付
        order.setUserId(loginId);
        order.setPayType(dto.getPayType());
        order.setTitle("课程【"+preCourseOrder.getCourseName()+"】订单");
        //insert(order);

        //==============创建订单明细
        //4.创建订单，保存数据库
        CourseOrderItem item = new CourseOrderItem();

        item.setAmount(preCourseOrder.getTotalAmount());
        item.setCount(1);
        item.setCreateTime(now);
        item.setCourseId(preCourseOrder.getCourseId());
        item.setCourseName(preCourseOrder.getCourseName());
        item.setCoursePic(preCourseOrder.getCoursePic());
        item.setOrderNo(preCourseOrder.getOrderNo());


        order.setTotalCount(1);
        order.getItems().add(item);

        Map<String, Object> map = new HashMap<>();
        map.put("loginId",loginId);
        map.put("courseId",preCourseOrder.getCourseId());
        Order2PayParamDto order2PayParamDto = new Order2PayParamDto(
                order.getTotalAmount(),
                dto.getPayType(),
                order.getOrderNo(),
                order.getUserId(),
                JSON.toJSONString(map),
                order.getTitle()
        );
        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(order2PayParamDto)).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxOrder2PayGroup",//同监听器组名
                "order-topic:payorder-tags",
                message,
                order
        );

        //执行本地事务的状态
        log.info("发送事务消息开始");
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        //发送消息状态
        SendStatus sendStatus = sendResult.getSendStatus();
        log.info("发送事务消息完成");

        if( localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            throw new GlobalException("下单失败！请重新下单","401");
        }
        //6.删除防重复token
        redisTemplate.delete(key);
        //7.删除预创订单
        redisTemplate.delete(orderNo);

        //7.发送支付超时取消订单
        try {
            Message<String> delayMessage = MessageBuilder.withPayload(order.getOrderNo()).build();
            SendResult syncSend = rocketMQTemplate.syncSend(
                    "cancel-order-topic:paytimeout-tags",
                    delayMessage,
                    30000,
                    4  //30秒
            );
            SendStatus sendSendStatus = syncSend.getSendStatus();
            if(sendSendStatus != SendStatus.SEND_OK){
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        return order.getOrderNo();
    }

}
